/**
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.solr.update;

import org.apache.lucene.index.*;
import org.apache.lucene.store.*;
import org.apache.lucene.util.Version;
import org.apache.solr.common.SolrException;
import org.apache.solr.core.DirectoryFactory;
import org.apache.solr.schema.IndexSchema;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;

/**
 * An IndexWriter that is configured via Solr config mechanisms.
 * 
 * @version $Id: SolrIndexWriter.java 1305693 2012-03-27 00:43:47Z janhoy $
 * @since solr 0.9
 */

public class SolrIndexWriter extends IndexWriter {
	private static Logger log = LoggerFactory.getLogger(SolrIndexWriter.class);

	public final static String LOCK_TYPE_SIMPLE = "simple";
	public final static String LOCK_TYPE_NATIVE = "native";
	public final static String LOCK_TYPE_SINGLE = "single";
	public final static String LOCK_TYPE_NONE = "none";

	String name;
	private PrintStream infoStream;

	public static Directory getDirectory(String path,
			DirectoryFactory directoryFactory, SolrIndexConfig config)
			throws IOException {

		Directory d = directoryFactory.open(path);

		String rawLockType = config.lockType;
		final String lockType = rawLockType.toLowerCase(Locale.ENGLISH).trim();

		if (LOCK_TYPE_SIMPLE.equals(lockType)) {
			// multiple SimpleFSLockFactory instances should be OK
			d.setLockFactory(new SimpleFSLockFactory(path));
		} else if (LOCK_TYPE_NATIVE.equals(lockType)) {
			d.setLockFactory(new NativeFSLockFactory(path));
		} else if (LOCK_TYPE_SINGLE.equals(lockType)) {
			if (!(d.getLockFactory() instanceof SingleInstanceLockFactory))
				d.setLockFactory(new SingleInstanceLockFactory());
		} else if (LOCK_TYPE_NONE.equals(lockType)) {
			// Recipe for disaster
			log.error("CONFIGURATION WARNING: locks are disabled on " + path);
			d.setLockFactory(NoLockFactory.getNoLockFactory());
		} else {
			throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
					"Unrecognized lockType: " + rawLockType);
		}
		return d;
	}

	public SolrIndexWriter(String name, String path,
			DirectoryFactory dirFactory, boolean create, IndexSchema schema,
			SolrIndexConfig config, IndexDeletionPolicy delPolicy)
			throws IOException {
		super(getDirectory(path, dirFactory, config), config
				.toIndexWriterConfig(schema)
				.setOpenMode(
						create ? IndexWriterConfig.OpenMode.CREATE
								: IndexWriterConfig.OpenMode.APPEND)
				.setIndexDeletionPolicy(delPolicy));

		if (config.maxFieldLength != -1)
			setMaxFieldLength(config.maxFieldLength);

		log.debug("Opened Writer " + name);
		this.name = name;

		String infoStreamFile = config.infoStreamFile;
		if (infoStreamFile != null) {
			File f = new File(infoStreamFile);
			File parent = f.getParentFile();
			if (parent != null)
				parent.mkdirs();
			FileOutputStream fos = new FileOutputStream(f, true);
			infoStream = new TimeLoggingPrintStream(fos, true);
			setInfoStream(infoStream);
		}
	}

	/**
	 * use DocumentBuilder now... private final void addField(Document doc,
	 * String name, String val) { SchemaField ftype = schema.getField(name);
	 * <p/>
	 * // we don't check for a null val ourselves because a solr.FieldType //
	 * might actually want to map it to something. If createField() // returns
	 * null, then we don't store the field.
	 * <p/>
	 * Field field = ftype.createField(val, boost); if (field != null)
	 * doc.add(field); }
	 * <p/>
	 * <p/>
	 * public void addRecord(String[] fieldNames, String[] fieldValues) throws
	 * IOException { Document doc = new Document(); for (int i=0;
	 * i<fieldNames.length; i++) { String name = fieldNames[i]; String val =
	 * fieldNames[i];
	 * <p/>
	 * // first null is end of list. client can reuse arrays if they want // and
	 * just write a single null if there is unused space. if (name==null) break;
	 * <p/>
	 * addField(doc,name,val); } addDocument(doc); } ****
	 */
	private volatile boolean isClosed = false;

	@Override
	public void close() throws IOException {
		log.debug("Closing Writer " + name);
		try {
			super.close();
			if (infoStream != null) {
				infoStream.close();
			}
		} finally {
			isClosed = true;
		}
	}

	@Override
	public void rollback() throws IOException {
		try {
			super.rollback();
		} finally {
			isClosed = true;
		}
	}

	@Override
	protected void finalize() throws Throwable {
		try {
			if (!isClosed) {
				log.error("SolrIndexWriter was not closed prior to finalize(), indicates a bug -- POSSIBLE RESOURCE LEAK!!!");
				close();
			}
		} finally {
			super.finalize();
		}

	}

	// Helper class for adding timestamps to infoStream logging
	class TimeLoggingPrintStream extends PrintStream {
		private DateFormat dateFormat;

		public TimeLoggingPrintStream(OutputStream underlyingOutputStream,
				boolean autoFlush) {
			super(underlyingOutputStream, autoFlush);
			this.dateFormat = DateFormat.getDateTimeInstance();
		}

		// We might ideally want to override print(String) as well, but
		// looking through the code that writes to infoStream, it appears
		// that all the classes except CheckIndex just use println.
		@Override
		public void println(String x) {
			print(dateFormat.format(new Date()) + " ");
			super.println(x);
		}
	}
}
